package nl.mad.drib.feeder;

import static java.lang.String.format;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

import nl.mad.drib.util.JdbcUtils;
import nl.mad.drib.util.sqlrunner.DefaultSqlScriptRunner;
import nl.mad.drib.util.sqlrunner.SqlScriptRunner;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;

/**
 * Feeds the database using native SQL statements.
 * 
 * @author Jeroen van Schagen
 * @since 04-05-2011
 */
public class SqlDatabaseFeeder extends SpringDatabaseFeeder {
    private static final Logger LOGGER = LoggerFactory.getLogger(SqlDatabaseFeeder.class);

    private SqlScriptRunner scriptRunner = new DefaultSqlScriptRunner();
    private DataSource dataSource;

    private ResourceLoader resourceLoader = new DefaultResourceLoader();
    private String scriptLocation = "classpath:import.sql";
    private boolean failOnScriptNotFound = true;

    /**
     * {@inheritDoc}
     */
    @Override
    public void perform() {
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
            runScriptOn(connection);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            JdbcUtils.closeQuietly(connection);
        }
    }

    /**
     * Execute the SQL script on our database.
     * @param connection open database connection
     */
    private void runScriptOn(Connection connection) {
        Reader scriptReader = null;
        try {
            scriptReader = createScriptReader();
            LOGGER.info("Executing import script: {}", scriptLocation);
            scriptRunner.runScript(scriptReader, connection);
        } catch (FileNotFoundException e) {
            if (failOnScriptNotFound) {
                throw new RuntimeException(e);
            } else {
                LOGGER.warn("Import script '{}' could not be found.", scriptLocation);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            IOUtils.closeQuietly(scriptReader);
        }
    }

    /**
     * Create a new reader that reads the statements in our script.
     * @return reader to the SQL script file
     * @throws FileNotFoundException whenever the file was not found
     * @throws IOException whenever the reader could not be opened
     */
    private Reader createScriptReader() throws IOException {
        Resource resource = resourceLoader.getResource(scriptLocation);
        if (!resource.exists()) {
            throw new FileNotFoundException(format("Import script '%s' could not be found.", scriptLocation));
        }
        return new InputStreamReader(resource.getInputStream());
    }

    public void setScriptRunner(SqlScriptRunner scriptRunner) {
        this.scriptRunner = scriptRunner;
    }

    @Required
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Required
    public void setScriptLocation(String scriptLocation) {
        this.scriptLocation = scriptLocation;
    }

    public void setFailOnScriptNotFound(boolean failOnScriptNotFound) {
        this.failOnScriptNotFound = failOnScriptNotFound;
    }

}
